package fxification.io;

import fxification.java.FxIO;
import java.lang.Throwable;
import java.nio.charset.Charset;

/***
<p>
Class designed to wrap functionalities of java.io at a high-level of abstraction.
This is not intended to be a fine-grained replication of the interface area of
the java.io classes, but rather a tactical presentation of the more coarse-grained
API classes.
</p>

<p>
<b>Supported API Classes</b>
<ul>
    <li>
        File.readeLines() - File.onReadLine, File.onReadLinesDone,<br/>
        File.doWithEachLine(function(line:String)):<br/>

        File.readBytes() - File.onReadBytes, File.onReadBytesDone<br/>
        File.doWithBytes(function(bytes:nativearray of Byte, length)<br/>

        File.traverseDir() - File.onEachFile, File.onTraverseDirDone<br/>
        File.dowWithEachFile(function(file:File))<br/>

        File.doWithInputStream(fn:function(:InputStream));<br/>
        File.doWithInputStreamReader(fn:function(:InputStreamReader));<br/>
        File.getInputStream():InputStream;<br/>
        File.getInputStreamReader():InputStreamReader;<br/>

    </li>

    <li>
        File.writeLines(lines:String[]) - File.onWriteLine, File.onWriteLinesDone
        File.writeBytes(bytes:nativearray of Byte) - File.onWriteBytes, File.onWriteBytesDone
        File.doWithOutputStream(fn:function(:OutputStream));
        File.doWithOutputStreamWriter(fn:function(:OutputStreamWriter));
        File.doWithPrintWriter(fn:function(:PrintWriter));
    </li>
</ul>

// Object Literal Forms
var io = IO.File {
    name:file_name
    onReadLines:function(line:String)
    onReadLinesDone:function(count:Long);
}
io.readLines();

// functional form
IO.withFile("FileName").doWithEachLine(function());
IO.withFile(File).doWithEachLineAsyncly(functio());
***/


/**
    Class used to wrap functionalities for Files
**/
public class File {
    public-init var name:String;
    public-init var nameFilter:String;

    public var onFailure:function(e:Throwable);
    public var onDone:function(count:Long);
    public var bufferSize:Integer = 4096;
    public var charset:String = java.nio.charset.Charset.defaultCharset().name();

    // Input
    public var onReadLine:function (line:String);
    public function readLines() {
        var stream = FxIO.getFileInputStream(name);

        Input {
            stream:stream
            charset:charset
            bufferSize:bufferSize
            onReadLine:onReadLine
            onDone:onDone
            onFailure:onFailure
        }.readLines();

        FxIO.closeInputStream(stream);
    }
    public function doWithEachLine(fn:function(line:String)){
        onReadLine = fn;
        onFailure = function(e:Throwable){
            e.printStackTrace();
        }
        readLines();
    }

    public var onReadBytes:function(bytes:nativearray of Byte, bytesRead:Integer);
    public function readBytes() {
        var stream = FxIO.getFileInputStream(name);
        
        Input{
            stream:stream,
            bufferSize:bufferSize,
            onReadBytes:onReadBytes,
            onDone:onDone,
            onFailure:onFailure
        }.readBytes();

        FxIO.closeInputStream(stream);
    }
    public function doWithBytes(fn:function(bytes:nativearray of Byte, bytesRead:Integer)){
        onReadBytes = fn;
        onFailure = function(e:Throwable){
            e.printStackTrace();
        }
        readBytes();
    }

    public var onEachFile:function(file:java.io.File);
    public function traverseDir() {
        FxIO.traverseDirectory(
        name,
        nameFilter,
        onEachFile,
        onDone,
        onFailure);
    }
    public function doWithEachFile(fn:function(file:java.io.File)){
        onEachFile = fn;
        onFailure = function(e:Throwable){
            e.printStackTrace();
        }
        traverseDir();
    }

    public function doWithInputStream(fn:function(stream:java.io.InputStream)){
        FxIO.doWithInputStream(FxIO.getFileInputStream(name), fn, onFailure);
    }

    public function doWithInputStreamReader(fn:function(reader:java.io.InputStreamReader)){
        FxIO.doWithInputStreamReader(FxIO.getFileInputStreamReader(name), fn, onFailure);
    }

    public function getInputStream():java.io.InputStream {
        FxIO.getFileInputStream(name);
    }

    public function getReader():java.io.InputStreamReader {
        FxIO.getFileInputStreamReader(name);
    }


    // Output
    public var onWriteLine: function(l:String);
    public function writeLines(lines:String[]) {
        var output = FxIO.getFileOutputStream(name);
        Output{
            stream:output,
            charset:charset,
            bufferSize:bufferSize,
            onWriteLine:onWriteLine,
            onDone:onDone,
            onFailure:onFailure}.writeLines(lines);
        FxIO.closeOutputStream(output);
    }

    public var onWriteByte:function (b:nativearray of Byte);
    public function writeBytes(bytes:nativearray of Byte) {
        var output = FxIO.getFileOutputStream(name);
        Output{
            stream:output,
            bufferSize:bufferSize,
            onWriteBytes:onWriteByte,
            onDone:onDone,
            onFailure:onFailure}.writeBytes(bytes);
        FxIO.closeOutputStream(output);
    }

    public function doWithOutputStream(fn:function(stream:java.io.OutputStream)):Void{
        FxIO.doWithOutputStream(FxIO.getFileOutputStream(name),fn,onFailure);
    }

    public function doWithOutputStreamWriter(fn:function(stream:java.io.OutputStreamWriter)){
        FxIO.doWithOutputStreamWriter(FxIO.getFileOutputStreamWriter(name),fn,onFailure);
    }

    public function doWithPrintWriter(fn:function(stream:java.io.PrintWriter)){
        FxIO.doWithPrintWriter(FxIO.getFilePrintWriter(name),fn,onFailure);
    }

    public function getOutputStream():java.io.OutputStream {
        FxIO.getFileOutputStream(name);
    }

    public function getStreamWriter():java.io.Writer {
        FxIO.getFileOutputStreamWriter(name);
    }

    public function getPrintWriter ():java.io.Writer {
        FxIO.getFilePrintWriter(name);
    }
}

public class Input {
    public-init var stream:java.io.InputStream;
    public-init var bufferSize:Integer = 4096;
    public-init var charset:String = Charset.defaultCharset().name();

    public var onFailure:function(e:Throwable);
    public var onDone:function(count:Long);

    public var onReadLine:function (line:String);
    public function readLines() {
        FxIO.readLines(
            stream,
            charset,
            bufferSize,
            onReadLine,
            onDone,
            onFailure
        );
    }
    public function doWithEachLine(fn:function(line:String)){
        onReadLine = fn;
        onFailure = function(e:Throwable){
            e.printStackTrace();
        }
        readLines();
    }

    public var onReadBytes:function(bytes:nativearray of Byte, bytesRead:Integer);
    public function readBytes() {
        FxIO.readBytes(
            stream,
            bufferSize,
            onReadBytes,
            onDone,
            onFailure);
    }
    public function doWithBytes(fn:function(bytes:nativearray of Byte, bytesRead:Integer)){
        onReadBytes = fn;
        onFailure = function(e:Throwable){
            e.printStackTrace();
        }
        readBytes();
    }

    public function getReader():java.io.Reader {
        new java.io.InputStreamReader(stream);
    }

}

public class Output {
    public-init var stream:java.io.OutputStream;
    public-init var bufferSize:Integer = 4096;
    public-init var charset:String = Charset.defaultCharset().name();

    public var onFailure:function(e:Throwable);
    public var onDone:function(count:Long);

    public var onWriteLine: function(l:String);
    public function writeLines(lines:String[]) {
        FxIO.writeLines(stream, charset, bufferSize, lines, onWriteLine, onDone, onFailure);
    }

    public var onWriteBytes:function (b:nativearray of Byte);
    public function writeBytes(bytes:nativearray of Byte) {
        FxIO.writeBytes(stream, bufferSize, bytes, onWriteBytes, onDone, onFailure);
    }

    public function getStreamWriter():java.io.OutputStreamWriter {
        new java.io.OutputStreamWriter(stream);
    }

    public function getPrintWriter():java.io.PrintWriter {
        new java.io.PrintWriter(stream);
    }

}

